
// 从数组的原型上拿到includes方法
const arrayInstrumentations = {};
['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
  const originMethod = Array.prototype[method]
  arrayInstrumentations[method] = function(...args) {
    // this是代理对象，先在代理对象中查找，将结果存储到res中
    let res = originMethod.apply(this, args)

    // res为false则说明没有找到，那么通过this.raw属性拿到原始数组，再去其中查找并更新res值
    if (res === false) {
      res = originMethod.apply(this.raw, args)
    }

    // 返回最终结果
    return res
  }
});
['push', 'pop', 'shift', 'splice','unshift'].forEach(method => {
  // 取得原始方法
  const originMethod = Array.prototype[method]
  // 重写
  arrayInstrumentations[method] = function(...args) {
    // 在调用原始方法之前，禁止追踪
    shouldTrack = false
    // push方法的默认行为
    let res = originMethod.apply(this, args)
    // 在调用原始方法之后，允许追踪
    shouldTrack = true
    // 返回最终结果
    return res
  }
});


/**
 * 创建响应式对象
 * @param {*} obj 原始对象
 * @param {*} isShallow 是否浅创建响应式对象
 * @returns
 */
function createReactive(obj, isShallow = false, isReadonly = false) {
  return new Proxy(obj, {
    // 拦截读取操作
    get(target, key, receiver) {
      console.log('get:', key)
      // 如果访问的属性是raw我们把原始值返回
      if (key === 'raw') {
        return target
      }
      if (key === 'size') {
        track(target, ITERATE_KEY)
        return Reflect.get(target, key, target)
      }
      if (Object.prototype.toString.call(target) === "[object Set]" || Object.prototype.toString.call(target) === "[object Map]") {
        return mutableInstrumentations[key]
      }
      if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      // 添加判断，如果key的类型是一个Symbol，则不进行追踪
      // 非只读的时候才需要建立响应联系
      if (!isReadonly && typeof key !== 'symbol') {
        // 追踪依赖
        track(target, key)
      }
      // 得到原始值返回结果
      const res = Reflect.get(target, key, receiver)
      // 如果是浅响应，我们直接返回原始值
      if (isShallow) {
        return res
      }
      // 追踪依赖
      track(target, key)
      // 如果原始值结果还是一个对象，我们递归调用reactive方法对其进行一层包装使其变为相应数据
      if (typeof res === 'object' && res !== null) {
        // 如果数据为只读，则调用readonly对值进行包装并返回，否则将结果包装成响应式数据返回
        return isReadonly ? readonly(res) : reactive(res)
      }
      // 返回res
      return res
    },
    // 拦截in操作符 'name' in p
    has(target, key) {
      track(target, key)
      return Reflect.has(target, key)
    },
    ownKeys(target) {
      // 如果操作目标target是数组，则使用length属性作为key并建立响应联系
      track(target, Array.isArray(target) ? 'length' : ITERATE_KEY)
      return Reflect.ownKeys(target)
    },
    set(target, key, newVal, receiver) {
      // 如果是只读的，则打印警告信息并返回
      if (isReadonly) {
        console.warn(`属性 ${key}是只读的`)
        return true
      }
      // 先获取旧值
      const oldVal = target[key]
      // type看看操作类型是添加新属性还是设置已有属性
      const type = Array.isArray(target)
        ? Number(key) < target.length
          ? 'SET'
          : 'ADD'
        : Object.prototype.hasOwnProperty.call(target, key)
        ? 'SET'
        : 'ADD'
      // 拿到执行结果
      const res = Reflect.set(target, key, newVal, receiver)
      // 如果两者相等，说明receiver是target的代理对象
      if (target === receiver.raw) {
        if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
          // 增加第四个参数，即触发响应的值
          trigger(target, key, type, newVal)
        }
      }
      return res
    },
    deleteProperty(target, key) {
      // 如果是只读的，则打印警告信息并返回
      if (isReadonly) {
        console.warn(`属性 ${key}是只读的`)
        return true
      }
      // 检查被操作的属性是否是对象自身的属性
      const hadKey = Object.prototype.hasOwnProperty.call(target, key)
      // 使用Reflect.deleteProperty完成属性的删除
      const res = Reflect.deleteProperty(target, key)

      if (res && hadKey) {
        // 只有当被删除的属性是自身属性且成功被删除时，才触发更新
        trigger(target, key, 'DELETE')
      }
      // 返回结果
      return res
    }
  })
}
// 创建深响应
function reactive(obj) {
  // 优先通过原始对象obj寻找之前创建的代理对象，如果找到了，就返回已有的代理对象
  const existionProxy = reactiveMap.get(obj)
  if (existionProxy) return existionProxy

  // 否则创建新的代理对象
  const proxy = createReactive(obj)

  // 存储到map中，从而避免重复创建
  reactiveMap.set(obj, proxy)

  return proxy
}

// 创建浅响应
function shallowReactive(obj) {
  return createReactive(obj, true)
}

// 创建深只读
function readonly(obj) {
  return createReactive(obj, false, true)
}
// 创建浅只读
function shallowReadonly(obj) {
  return createReactive(obj, true, true)
}
